package com.someok.web.tags.form;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;

import com.someok.utils.BeanUtil;
import com.someok.utils.HtmlUtil;
import com.someok.utils.StringUtil;
import com.someok.utils.codec.HtmlEncoder;

/**
 * Form tag populates a included form with values from the one or more beans.
 * Beans are stored in various scopes. This tag is <i>smart</i> so everything
 * happens automatically. However, there are several easy-to-work-with limits
 * that must be fulfilled to make this tag working properly:
 * <ul>
 * <li>All values attributes must not exist.</li>
 * <li>Textarea content must be empty.</li>
 * <li>All<sup>*</sup> attributes and its names must be directly connected
 * with the <code>="</code> or <code>='</code>.</li>
 * <li>All<sup>*</sup> attributes values must be surrounded by quotes (" or
 * ').</li>
 * <li>of course, there should be no duplicated names, both across the form and
 * beans.</li>
 * </ul>
 *
 * <sup>*</sup>note: not all, just: name, value and type attributes, however,
 * it is recommended to use the above style everywhere.
 * <p>
 *
 * Availiable scopes are:
 * <ul>
 * <li>page,</li>
 * <li>session, and</li>
 * <li>request</li>
 * </ul>
 *
 * <p>
 * <a href="FormTag.java.html"><i>View Source</i></a>
 * </p>
 *
 * @modify-traces
 * <ul>
 * <li>2006-2-23 : bean中 get* 方法对应的属性名（get方法中除 "get" 之外的字符串）必须与表单中各个域的 name 一致，不过大小写不敏感。</li>
 * <li>testaaa</li>
 * </ul>
 *
 * @version $Revision: 76 $ $Date: 2006-07-07 01:03:18 +0800 (星期五, 07 七月 2006) $
 */
public class FormTag extends BodyTagSupport {

	private static final long serialVersionUID = -940128377472364548L;

	// ---------------------------------------------------------------- tag
	// parameters

	private String beanNames = null;

	/**
	 * Sets bean names with value of the "bean" attribute.
	 *
	 * @param v
	 *            bean names
	 */
	public void setBeans(String v) {
		beanNames = v;
	}

	/**
	 * Gets bean names.
	 *
	 * @return bean names
	 */
	public String getBeans() {
		return beanNames;
	}

	private String scopes = null;

	/**
	 * Sets the value of "scope" attribute, that represent beans scope.
	 *
	 * @param v
	 */
	public void setScopes(String v) {
		scopes = v;
	}

	/**
	 * Return value of the "scope" attribute
	 *
	 * @return bean scopes
	 */
	public String getScopes() {
		return scopes;
	}

	// ---------------------------------------------------------------- tag
	// methods

	private HashMap beansValues = null;

	/**
	 * Copies properties of all specified bean into one map.
	 *
	 * @return EVAL_BODY_AGAIN
	 */
	public int doStartTag() {
		beansValues = new HashMap();
		String[] b = StringUtil.splitc(beanNames, ", ");
		String[] s = StringUtil.splitc(scopes.toLowerCase(), ", ");

		HttpServletRequest request = (HttpServletRequest) pageContext
				.getRequest();
		HttpSession session = (HttpSession) pageContext.getSession();

		for (int i = 0; i < b.length; i++) {
			Object bean = null;
			if ((s[i].length() == 0) || (s[i].equals("page"))) {
				bean = pageContext.getAttribute(b[i]);
			} else if (s[i].equals("request")) {
				bean = request.getAttribute(b[i]);
			} else if (s[i].equals("session")) {
				bean = session.getAttribute(b[i]);
			}
			if (bean != null) {
				beansValues.putAll((Map) BeanUtil.getAllProperties(bean));
			}

		}
		return EVAL_BODY_AGAIN;
	}

	/**
	 * Performs smart form population.
	 *
	 * @return SKIP_BODY
	 */
	public int doAfterBody() {
		BodyContent body = getBodyContent();
		try {
			JspWriter out = body.getEnclosingWriter();
			String bodytext = body.getString();
			if ((beansValues != null) && (beansValues.size() > 0)) {
				bodytext = populateForm(bodytext, beansValues);
			}
			out.print(bodytext);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return SKIP_BODY;
	}

	/**
	 * End of tag.
	 *
	 * @return EVAL_PAGE
	 */
	public int doEndTag() {
		return EVAL_PAGE;
	}

	// ---------------------------------------------------------------- populate

	/**
	 * 将从 session 或是 request 的 attribute 中得到的 bean 中的属性对应的值注入表单.
	 *
	 */
	private String populateForm(String html, HashMap values) {

		int i = 0, s = 0;
		StringBuffer result = new StringBuffer(html.length());
		String currentSelectName = null;
		while (true) {
			// find starting tag
			i = html.indexOf('<', s);
			if (i == -1) {
				result.append(html.substring(s));
				break; // input tag not found
			}
			result.append(html.substring(s, i)); // tag found, all before tag
													// is stored
			s = i;

			// find closing tag
			i = html.indexOf('>', i);
			if (i == -1) {
				result.append(html.substring(s));
				break; // closing tag not found
			}
			i++;

			// match tags
			String tag = html.substring(s, i);
			String tagName = HtmlUtil.getTagName(tag);
			if (tagName.equalsIgnoreCase("input") == true) {
				String tagType = HtmlUtil.getAttribute(tag, "type");
				if (tagType != null) {
					String name = getUpperCaseName(HtmlUtil.getAttribute(tag, "name"));
					if (values.containsKey(name)) {
						String value = StringUtil.toString(values.get(name));
						tagType = tagType.toLowerCase();
						if (tagType.equals("text")) {
							tag = HtmlUtil.addAttribute(tag, "value", value);
						}
						if (tagType.equals("hidden")) {
							tag = HtmlUtil.addAttribute(tag, "value", value);
						}
						if (tagType.equals("image")) {
							tag = HtmlUtil.addAttribute(tag, "value", value);
						}
						if (tagType.equals("password")) {
							tag = HtmlUtil.addAttribute(tag, "value", value);
						}
						if (tagType.equals("checkbox")) {
							String tagValue = HtmlUtil.getAttribute(tag,
									"value");
							if (tagValue == null) {
								tagValue = "true";
							}
							if (tagValue.equals(value)) {
								tag = HtmlUtil.addAttribute(tag, "checked");
							}
						}
						if (tagType.equals("radio")) {
							String tagValue = HtmlUtil.getAttribute(tag,
									"value");
							if (tagValue != null) {
								if (tagValue.equals(value)) {
									tag = HtmlUtil.addAttribute(tag, "checked");
								}
							}
						}
					}
				}
			} else if (tagName.equalsIgnoreCase("textarea") == true) {
				String name = getUpperCaseName(HtmlUtil.getAttribute(tag, "name"));
				if (values.containsKey(name)) {
					Object value = values.get(name);
					if (value != null) {
						// tag +=
						// ServletUtil.encodeHtml(StringUtil.toString(value));
						tag += HtmlEncoder.encode(StringUtil.toString(value));
					}
				}
			} else if (tagName.equalsIgnoreCase("select") == true) {
				currentSelectName = getUpperCaseName(HtmlUtil.getAttribute(tag, "name"));
			} else if (tagName.equalsIgnoreCase("/select") == true) {
				currentSelectName = null;
			} else if (tagName.equalsIgnoreCase("option") == true) {
				if (currentSelectName != null) {
					String tagValue = HtmlUtil.getAttribute(tag, "value");
					if (tagValue != null) {
						if (values.containsKey(currentSelectName)) {
							Object vals = values.get(currentSelectName);
							if (vals != null) {
								if (vals.getClass().isArray() == false) {
									String value = StringUtil.toString(vals);
									if (value.equals(tagValue)) {
										tag = HtmlUtil.addAttribute(tag,
												"selected");
									}
								} else {
									String vs[] = StringUtil
											.toStringArray(vals);
									for (int k = 0; k < vs.length; k++) {
										String vsk = vs[k];
										if (vsk != null) {
											if (vsk.equals(tagValue)) {
												tag = HtmlUtil.addAttribute(
														tag, "selected");
											}
										}
									}
								}
							}
						}
					}
				}
			}
			result.append(tag);
			s = i;
		}
		return result.toString();
	}

	/**
	 * 返回给定参数的大写形式.
	 * @param name
	 * @return
	 */
	private String  getUpperCaseName(String name) {
		return StringUtil.nvl(name).toUpperCase();
	}
}
